package ip

import (
	"fmt"
	"gitee.com/liumou_site/logger"
	"github.com/spf13/cast"
	"net"
	"regexp"
	"sort"
	"strings"
)

// GetIntranetAddressList 获取局域网IP地址列表
// 返回值:
//   - bool: 表示是否成功获取到IP地址列表，true表示成功，false表示失败
//   - []string: 包含局域网IP地址的切片
func GetIntranetAddressList() (bool, []string) {
	// 初始化IP地址切片
	var IpList []string

	// 获取所有网络接口的地址，如果出错则记录日志并返回错误
	address, err := net.InterfaceAddrs()
	if err != nil {
		logger.Error("GetIntranetAddress failed")
		// 返回空的IP地址列表
		return false, strings.Split(" ", " ")
	}

	// 遍历所有地址，筛选出非回环的IPv4地址
	for _, addr := range address {
		// 检查地址是否为IP网络地址，且不是回环地址
		if IpNet, ok := addr.(*net.IPNet); ok && !IpNet.IP.IsLoopback() {
			// 检查是否为IPv4地址
			if IpNet.IP.To4() != nil {
				// 获取IP地址并添加到列表中
				ip := IpNet.IP.String()
				//IpList = append(IpList, ip)
				// 记录日志
				logger.Info("IP: ", ip)
			}
		}
	}

	// 返回成功标志和IP地址列表
	return true, IpList
}

// GetUseIP 通过指定的DNS服务器获取本地IP地址。
// 返回本地IP地址的字符串表示，如果获取失败则返回错误。
func GetUseIP() (string, error) {
	// 定义DNS服务器列表
	dnsList := []string{"8.8.8.8", "119.29.29.29"}
	for _, dns := range dnsList {
		// 构造DNS服务器地址，格式为IP:端口
		server := string(dns) + ":" + cast.ToString(53)
		// 尝试与DNS服务器建立UDP连接
		conn, err := net.Dial("udp", server)
		// 获取本地IP
		if err != nil {
			// 延迟函数
			defer func(conn net.Conn) {
				err := conn.Close()
				if err != nil {
					return
				}
			}(conn)
			logger.Error("获取失败,请检查网络是否正常连接网络: ", err)
			if dns == "119.29.29.29" {
				return "failed", err
			}
		} else {
			LocalIpInfo := conn.LocalAddr().String()
			// 获取:号的index值，然后截取
			LocalIp := strings.LastIndex(LocalIpInfo, ":")
			logger.Debug("本地请求信息: ", LocalIpInfo)
			logger.Debug(":号索引值: ", LocalIp)
			err := conn.Close()
			if err != nil {
				return "", err
			}
			return LocalIpInfo[0:LocalIp], nil
		}
	}
	return "failed", fmt.Errorf("获取异常")
}

// GenerateList 生成指定子网范围内的一系列IP地址
// 参数:
//
//	subnet: IP子网的前缀部分，例如"192.168.1"
//	start: IP地址的起始范围
//	stop: IP地址的结束范围
//
// 返回值:
//
//	[]string: 生成的IP地址列表
//	error: 如果传入的初始值大于或等于终止值，或者子网格式不正确，则返回错误
func GenerateList(subnet string, start, stop uint8) ([]string, error) {
	// 初始化IP列表
	var IpList []string

	// 将子网字符串按点分割，以便处理
	qp := strings.Split(subnet, ".")

	// 检查起始和终止值的逻辑关系
	if start >= stop {
		// 如果初始值大于或等于终止值，则生成错误信息并返回
		err := fmt.Errorf("传入的初始值[ %d ]大于或等于终止值[ %d ]", start, stop)
		return qp, err
	}

	// 生成一个测试IP，用于验证子网格式
	TestIp := subnet + ".2"

	// 检测生成的IP是否符合IPV4规范
	_, s := Parse(TestIp)
	if s == 4 {
		// 如果符合IPV4规范则开始生成IP列表
		logger.Debug("生成IP列表...")
		for index_ := start; index_ < stop; index_++ {
			IpList = append(IpList, fmt.Sprintf("%s.%d", subnet, index_))
		}

		// 如果终止值为255，添加子网的广播地址
		if stop == 255 {
			IpList = append(IpList, fmt.Sprintf("%s.255", subnet))
		}

		// 返回生成的IP列表
		return IpList, nil
	}

	// 如果不符合规范则返回错误信息
	logger.Error("生成的测试IP: ", TestIp)
	return qp, fmt.Errorf("传入了非标准IP地址,标准网段参考: 192.168.1")
}

// CutSubnet 提取IP地址的网段部分。
// 该函数接收一个net.IP类型的参数ip，表示一个IP地址。
// 函数返回一个字符串，表示提取出的网段部分。
func CutSubnet(ip net.IP) string {
	// 将IP地址字符串按"."分割为四部分
	s := strings.Split(ip.String(), ".")
	// 提取前三个部分作为网段
	subs := s[:3]
	// 将网段的部分合并为一个字符串
	subnet := SliceToString(subs, ".")
	// 返回合并后的网段字符串
	return subnet
}

// CutEndId 提取并返回IPv4地址的最后一部分。
// 参数 ip 是一个字符串类型的IP地址。
// 返回值 index 是IP地址的最后一部分转换为整数后的结果。
// 如果IP地址格式不正确，则返回错误 err。
func CutEndId(ip net.IP) (index int, err error) {
	// 预设结果
	err = fmt.Errorf("[ %s ]IP格式不正确: 仅支持IPV4", ip.String())
	index = -1
	// 判断这个地址是否为V4
	ip = ip.To4()

	if ip == nil {
		logger.Error(err)
		return
	}
	// 将IPv4地址按"."分割为字符串切片。
	s := strings.Split(ip.String(), ".")

	// 提取最后一部分子字符串。
	subs := s[3]

	// 将子字符串转换为整数。
	index = cast.ToInt(subs)

	// 返回处理结果。
	return index, nil
}

// Sort 函数接收一个字符串切片并将其转换为排序(升序)的net.IP切片。
// 它返回排序后的IP地址切片和一个错误，如果输入无效则错误不为nil。
// ip: 待排序的IP地址字符串切片。
// 返回值res: 排序后的net.IP切片。
// 返回值err: 如果输入无效则返回错误。
func Sort(ip []net.IP) (res []net.IP, err error) {
	// 检查传入的IP列表是否为nil或空，如果是，则返回错误。
	if ip == nil || len(ip) == 0 {
		return nil, fmt.Errorf("传入的IP列表为空")
	}
	// 初始化结果切片与输入切片相同长度。
	res = make([]net.IP, len(ip))
	for i, v := range ip {
		if v == nil {
			// 如果某个IP地址为nil，则返回错误。
			return nil, fmt.Errorf("传入的IP列表中存在nil值")
		}
		if v.To4() == nil {
			return nil, fmt.Errorf("传入的IP列表中存在非IPV4地址")
		}
		res[i] = v
	}

	// 定义一个自定义的排序函数，用于按字节顺序排序IPv4地址。
	sort.Slice(res, func(i, j int) bool {
		return CompareIP(res[i].To4(), res[j].To4()) < 0
	})

	// 返回排序后的IP地址切片和nil错误。
	return res, nil
}

// GenerateIndexEnd 生成一个IP地址列表，这些IP地址是通过在给定IP地址的子网上添加索引生成的。
// 该函数接收一个IP地址和三个索引（start, stop, end），并生成一个IP地址列表，这些IP地址的最后一位是根据子网和索引计算得出的。
// 参数:
//
//	ip (net.IP): 基础IP地址 10.1.8.8
//	start (int): 起始索引 0
//	stop (int): 终止索引 11
//	end (int): 结尾数字，与索引组合以生成IP地址 8
//
// 返回值:
//
//	ips ([]net.IP): 生成的IP地址列表 [10.1.8.8 10.1.8.18 10.1.8.28 10.1.8.38 10.1.8.48 10.1.8.58 10.1.8.68 10.1.8.78 10.1.8.88 10.1.8.98 10.1.8.108 10.1.8.118]
//	err (error): 错误信息，如果生成过程中遇到任何问题，则返回错误
func GenerateIndexEnd(ip net.IP, start, stop, end int) (ips []net.IP, err error) {
	// 初始化IP地址列表
	var ipsList []net.IP

	// 检查传入的索引值是否在合法范围内
	if start < 0 || stop < 0 || end < 0 || start > stop || stop > 255 || end > 255 {
		return nil, fmt.Errorf("传入的参数有误,数值只能在0~255之间")
	}

	// 确保传入的IP地址是有效的IPv4地址
	ip = ip.To4()
	if ip == nil {
		return nil, fmt.Errorf("传入的IP地址无效")
	}

	// 获取IP地址的子网部分
	subNet := CutSubnet(ip)

	// 从起始索引循环到终止索引，生成IP地址
	for index := start; index <= stop; index++ {
		// 生成新的IP地址字符串，并尝试将其解析为net.IP类型
		ipTmp := fmt.Sprintf("%s.%d%d", subNet, index, end)
		if index == 0 {
			ipTmp = fmt.Sprintf("%s.%d", subNet, end)
		}
		ipTmp_, _ := Parse(ipTmp)

		// 如果解析失败，返回错误
		if ipTmp_ == nil {
			return nil, fmt.Errorf("IP类型转换异常: %s", ipTmp)
		}

		// 将解析成功的IP地址添加到IP地址列表中
		ipsList = append(ipsList, ipTmp_)
	}

	// 返回生成的IP地址列表和nil错误
	return ipsList, nil
}

// GenerateIndex 根据给定的IP地址和索引列表生成一个IP列表。
// 该函数接收一个IP地址和一个索引列表，然后根据这些索引生成对应的IP地址列表。
// 参数:
//
//	ip - 基础IP地址，用于生成IP列表。
//	ipSlice - 包含一系列索引的切片，用于指定生成IP地址的位置。
//
// 返回值:
//
//	IpList - 生成的IP地址列表。
//	err - 如果生成过程中发生错误，返回该错误。
func GenerateIndex(ip net.IP, ipSlice []int) (IpList []net.IP, err error) {
	var sub string
	var ips int
	var ipRes net.IP

	// 检查ipSlice是否为空，如果为空，则返回错误
	if len(ipSlice) == 0 {
		err = fmt.Errorf("传入列表为空")
		logger.Error(err)
		return nil, err
	}

	// 检查传入的IP地址是否为IPv4地址
	if ip.To4() == nil {
		err = fmt.Errorf("传入的IP地址非IPV4: %s", ip)
		logger.Error(err)
		return nil, err
	}

	// 切割子网，获取IP地址的子网部分
	sub = CutSubnet(ip)

	// 对索引列表进行排序，确保索引顺序
	sort.Ints(ipSlice)

	// 遍历排序后的索引列表，生成IP地址
	for _, ipIndex := range ipSlice {
		ipTmp := sub + "." + cast.ToString(ipIndex)
		ipRes, ips = Parse(ipTmp) // 解析IP地址

		// 检查解析后的IP地址是否为有效的IPv4地址
		if ips == 4 {
			IpList = append(IpList, ipRes)
		} else {
			// 如果生成的IP地址格式不正确，记录错误日志并返回错误
			logger.Error("IP格式不正确: ", ipTmp)
			return nil, fmt.Errorf("IP格式不正确: %s", ipTmp)
		}
	}

	// 返回生成的IP列表和可能的错误
	return IpList, nil
}

// Parse 解析给定的字符串s，将其转换为IP地址和相应的版本。
// 如果字符串s是一个有效的IP地址，函数将返回该IP地址和其版本（4表示IPv4，6表示IPv6）。
// 如果字符串s不是有效的IP地址格式，函数将返回nil和0。
func Parse(s string) (net.IP, int) {
	// 使用net.ParseIP方法将字符串s转换为IP地址。
	ip := net.ParseIP(s)
	// 如果返回零值则判断格式不正确
	if ip == nil {
		return nil, 0
	}
	// 判断是否为IPv4地址
	if ip.To4() != nil {
		return ip, 4
	}
	// 如果不是IPv4地址，则默认为IPv6地址
	return ip, 6
}

// StrGetAddr 函数从给定文本中提取所有IPv4地址。
// 参数
//
//	text是待处理的字符串。
//
// 返回值
//
//	error类型和net.IP切片，错误用于指示是否成功提取IP地址。
func StrGetAddr(text string) (err error, res []net.IP) {
	// 初始化一个空的IP地址切片，用于存储提取的IPv4地址。

	// 定义一个正则表达式，用于匹配IPv4地址模式。
	regex := regexp.MustCompile(`\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b`)

	// 使用正则表达式查找所有匹配的字符串。
	matches := regex.FindAllString(text, -1)

	// 检查是否有匹配项。
	if len(matches) >= 1 {
		// 遍历所有匹配项。
		for _, match := range matches {
			// Parse函数用于解析字符串为IP地址。
			// 这里假设Parse函数存在并且返回两个值：解析后的IP地址和地址族类型。
			r, n := Parse(match)
			// 检查是否为IPv4地址。
			if n == 4 {
				// 将解析后的IPv4地址添加到结果切片中。
				res = append(res, r)
			}
		}
		// 检查是否至少有一个IPv4地址被添加到结果中。
		if len(res) >= 1 {
			// 成功，返回nil和包含所有提取的IPv4地址的切片。
			return nil, res
		}
	}
	// 如果没有IPv4地址被提取，返回错误信息和空的IP地址切片。
	err = fmt.Errorf("匹配失败")
	logger.Error(err)
	return
}

// RangeCidr 接受一个CIDR字符串，返回子网的起始IP和结束IP
// 例如： 10.1.0.0/21
//
//	返回值
//
//		10.1.0.0
//		10.1.7.255
//		nil
func RangeCidr(cidr string) (net.IP, net.IP, error) {
	_, ipv4Net, err := net.ParseCIDR(cidr)
	if err != nil {
		logger.Error(err)
		return nil, nil, err
	}
	// 获取网络地址
	startIP := ipv4Net.IP
	// 计算广播地址：将网络掩码反转并与网络地址进行 OR 操作
	mask := ipv4Net.Mask
	endIP := make(net.IP, len(startIP))
	for i := 0; i < len(startIP); i++ {
		endIP[i] = startIP[i] | ^mask[i]
	}
	return startIP, endIP, nil
}

// RangeDetailedList 根据起始IP和结束IP生成IP地址列表
//
//	startIp: 起始IP地址
//	endIp: 结束IP地址
//
// 返回值:
//
//	res: 一个映射，键为网段，值为该网段下的IP地址列表
//	IpList: 所有生成的IP地址的字符串列表
//	error: 如果发生错误，返回错误信息
func RangeDetailedList(cidr string) (map[string][]net.IP, []string, error) {
	// RangeCidr 根据CIDR块返回起始IP和结束IP。
	// cidr: 一个CIDR块，如"192.168.1.0/24"。
	// 返回值: 起始IP地址, 结束IP地址, 错误信息（如果有）。
	startIp, endIp, err := RangeCidr(cidr)
	if err != nil {
		logger.Error(err)
		return nil, nil, err
	}

	// sub 用于存储子网掩码信息。
	var sub string

	// IpList 用于存储IP地址的字符串列表。
	var IpList []string

	// ipsTmp 用于临时存储IP地址的net.IP切片。
	var ipsTmp []net.IP

	// res 用于存储网段和对应的IP地址列表的映射。
	// 网段: IP清单
	var res map[string][]net.IP
	res = make(map[string][]net.IP)
	logger.Debug("开始生成IP地址列表...")
	logger.Debug("开始IP: ", startIp, "结束IP: ", endIp)

	// 将开始IP地址的四个字节转换为整数，以便进行计算。
	startIpInt := toInt(startIp)
	endIpInt := toInt(endIp)

	// 如果开始IP和结束IP相同，则直接返回包含开始IP的列表。
	if startIp.Equal(endIpInt) {
		logger.Debug(fmt.Sprintf("开始IP[ %s ]和结束IP[ %s ]相同，直接返回", startIp, endIp))
		return res, []string{startIp.String()}, nil
	}

	// 如果开始IP大于结束IP，则返回错误。
	if Compare(startIp, endIp) {
		logger.Error(fmt.Sprintf("开始IP[ %s ]大于结束IP[ %s ]", startIp, endIp))
		return res, nil, fmt.Errorf("开始IP[ %s ]大于结束IP[ %s ]", startIp, endIp)
	}
	// 检查开始IP和结束IP的前三段是否相同，如果相同，则生成IP列表
	if CutSubnet(startIp) == CutSubnet(endIp) {
		logger.Debug("开始IP和结束IP的前三段相同，生成IP列表")
		logger.Debug("开始IP: ", startIpInt[3], "结束IP: ", endIpInt[3])
		list, err := GenerateList(CutSubnet(startIp), startIpInt[3], endIpInt[3])
		// 如果生成IP列表时发生错误，记录错误并返回
		if err != nil {
			logger.Error(err)
			return nil, nil, err
		}
		res[CutSubnet(startIp)] = StrListToIpList(list)
		return res, list, err
	}

	// 比较开始IP和结束IP的前两段是否相同
	s, err := CompareS(startIp, endIp, 2)
	if err != nil {
		return nil, nil, err
	}
	// 如果前两段不相同，记录错误并返回
	if !s {
		logger.Error("开始IP和结束IP的前两段不相同,IP范围过大")
		return res, nil, fmt.Errorf("开始IP和结束IP的前两段不相同,IP范围过大")
	}

	// 如果开始IP和结束IP的前两段相同，则生成IP列表。
	logger.Debug("开始IP和结束IP的前两段相同，生成IP列表")
	sub = fmt.Sprintf("%d.%d", startIpInt[0], startIpInt[1])
	logger.Debug(sub)
	// 遍历IP地址的第三个字节，从startIpInt到endIpInt
	for ip3 := startIpInt[2]; ip3 <= endIpInt[2]; ip3++ {
		// 构建子网掩码字符串，格式为x.x.x.
		sub_ := sub + "." + cast.ToString(ip3)

		// 当第三个字节既不是起始也不是结束值时，生成该子网内的所有IP地址
		if ip3 != startIpInt[2] && ip3 != endIpInt[2] {
			// 生成IP地址列表，排除起始和结束IP的特殊情况
			ips_, err := GenerateList(sub_, uint8(1), uint8(255))
			logger.Debug(ips_)
			if err == nil {
				// 将生成的IP地址添加到总的IP列表中
				IpList = append(IpList, ips_...)
				// 将字符串形式的IP地址转换为IP列表
				ipsTmp = StrListToIpList(ips_)
			}
		} else {
			// 处理起始和结束IP地址的特殊情况
			if ip3 == startIpInt[2] {
				// 如果是起始IP地址，从起始IP的第四个字节开始到255
				ips_, err := GenerateList(sub_, startIpInt[3], uint8(255))
				logger.Debug(ips_)
				if err == nil {
					IpList = append(IpList, ips_...)
					ipsTmp = StrListToIpList(ips_)
				}
			} else {
				// 如果是结束IP地址，从1到结束IP的第四个字节
				if ip3 == endIpInt[2] {
					ips_, err := GenerateList(sub_, uint8(1), endIpInt[3])
					if err == nil {
						IpList = append(IpList, ips_...)
						ipsTmp = StrListToIpList(ips_)
					}
				}
			}
		}
		// 将子网掩码字符串作为键，对应的IP列表作为值，存储在结果映射中
		res[sub_] = ipsTmp
	}
	// 返回结果映射，总的IP列表，和一个空的错误值
	return res, IpList, nil
}
